#include "ChangeCameraAnimator.h"
#include "CGlobals.h"
#include "CLevelEditor.h"
#include "CUserInterface.h"
#include "DoUndoCommands.h"
#include "CSceneObject.h"
#include "irrlicht.h"
#include <cmath>

extern CGlobals* g;
extern CLevelEditor* editor;
extern CUserInterface* ui;

CChangeCameraAnimator::CChangeCameraAnimator (ICameraSceneNode* camera)
	:m_camera(camera),
	 m_cmd(0),
	 m_leftMousePressed(false),
	 m_rightMousePressed(false),
	 m_middleMousePressed(false),
	 m_shiftPressed(false),
	 m_ctrlPressed(false)
{		
}

bool CChangeCameraAnimator::isEventReceiverEnabled () const
{
	return true;
}

bool CChangeCameraAnimator::OnEvent (const SEvent& event)
{
	const float k_cam_speed_normal = 0.5f;
	const float k_cam_speed_quick = 4.0f;

	switch (event.EventType)
	{
		case EET_KEY_INPUT_EVENT:
			if(event.KeyInput.Shift)
				m_shiftPressed = true;
			else
				m_shiftPressed = false;

			if(event.KeyInput.Control)
				m_ctrlPressed = true;
			else
				m_ctrlPressed = false;

			if (event.KeyInput.PressedDown)
			{
				float cam_speed = k_cam_speed_normal;
				if(event.KeyInput.Shift)
					cam_speed = k_cam_speed_quick;

				switch (event.KeyInput.Key)
				{
					case KEY_KEY_W:
						m_targetPos.Y += 50.0f;
						break;
					case KEY_KEY_S:
						m_targetPos.Y -= 50.0f;
						break;
					case KEY_KEY_A:
						m_targetPos.X -= 50.0f;
						break;
					case KEY_KEY_D:
						m_targetPos.X += 50.0f;
						break;
					case KEY_KEY_Q:
						m_targetPos.Z += 50.0f;
						break;
					case KEY_KEY_E:
						m_targetPos.Z -= 50.0f;
						break;
					case KEY_LEFT:					
						m_yaw+=cam_speed;
						break;					

					case KEY_RIGHT:					
						m_yaw-=cam_speed;
						break;					

					case KEY_UP:					
						m_pitch+=cam_speed;
						break;					

					case KEY_DOWN:					
						m_pitch-=cam_speed;
						break;					

					case KEY_HOME:					
						m_camDis-=cam_speed*2;
						break;					

					case KEY_END:					
						m_camDis+=cam_speed*2;
						break;										
				}

				updateCamera();
			}
			break;

		case EET_MOUSE_INPUT_EVENT:
			switch (event.MouseInput.Event)
			{
				case EMIE_LMOUSE_PRESSED_DOWN:
					m_leftMousePressed = true;
					m_mouseStartPos = irr::core::position2di(event.MouseInput.X, event.MouseInput.Y);
					break;
				case EMIE_LMOUSE_LEFT_UP:
					m_leftMousePressed = false;
					break;
				case EMIE_RMOUSE_PRESSED_DOWN:
					m_rightMousePressed = true;
					break;
				case EMIE_MMOUSE_PRESSED_DOWN:
					m_middleMousePressed = true;
					break;
				case EMIE_RMOUSE_LEFT_UP:
					m_rightMousePressed = false;
					break;
				case EMIE_MMOUSE_LEFT_UP:
					m_middleMousePressed = false;
					break;
				case EMIE_MOUSE_MOVED:
					if (m_leftMousePressed)
					{
						position2di mouse = position2di(event.MouseInput.X, event.MouseInput.Y);
						position2di diff = mouse - m_mouseStartPos;
						if(m_shiftPressed)
							diff.Y=0;
						if(m_ctrlPressed)
							diff.X=0;
						m_yaw-=0.15f*diff.X;
						m_pitch-=0.15f*diff.Y;						
					}					
					else if (m_rightMousePressed || m_middleMousePressed)
					{
						position2di mouse = position2di(event.MouseInput.X, event.MouseInput.Y);
						position2di _diff = mouse - m_mouseStartPos;						
						position2df diff;
						diff.X = static_cast<f32>(_diff.X);
						diff.Y = static_cast<f32>(_diff.Y);

						if(m_shiftPressed)
							diff.Y=0;
						if(m_ctrlPressed)
							diff.X=0;
						
						irr::core::vector3df up = m_camera->getUpVector();
						irr::core::vector3df right = -m_camDir.crossProduct(up);
						right.normalize();
						up = -right.crossProduct(m_camDir);
						up.normalize();

						float speedFactor = 2.0f;
						diff.X*=speedFactor;
						diff.Y*=speedFactor;

						if(m_rightMousePressed)
							m_targetPos += (-diff.X*right + diff.Y*up);						
						else
							m_targetPos += m_camDir*diff.Y*2.0f;
						
					}
					m_mouseStartPos = irr::core::position2di(event.MouseInput.X, event.MouseInput.Y);
					break;
				case EMIE_MOUSE_WHEEL:
					irr::f32 dwheel = event.MouseInput.Wheel;
					m_camDis+=dwheel*100;
					break;
			}

			updateCamera();
			break;
	}

	// if nobody processed the event, return false
	return false;
}

void CChangeCameraAnimator::animateNode (ISceneNode* node, u32 timeMs)
{
	updateCamera();
}

void CChangeCameraAnimator::setAttribute (const c8* attribute, float value)
{
	if (m_cmd!=0)
	{
		m_cmd->m_Attributes->setAttribute(attribute, value);		
	}	
}

void CChangeCameraAnimator::setAttribute (const c8* attribute, const irr::core::vector3df& value)
{
	if (m_cmd!=0)
	{
		m_cmd->m_Attributes->setAttribute(attribute, value);		
	}
}		

float CChangeCameraAnimator::getAttributeAsFloat (const c8* attribute)
{
	if (m_cmd!=0)
	{
		return m_cmd->m_Attributes->getAttributeAsFloat(attribute);
	}
	return 0.0f;
}

irr::core::vector3df CChangeCameraAnimator::getAttributeAsVector3D(const c8* attribute)
{
	if (m_cmd!=0)
	{
		return m_cmd->m_Attributes->getAttributeAsVector3d(attribute);
	}
	return irr::core::vector3df();
}

void CChangeCameraAnimator::SetCameraInfo( CCinematicCommand* cmd )
{	
	m_cmd = cmd;	

	m_targetPos = getAttributeAsVector3D("target");
	m_camDir = getAttributeAsVector3D("dir"); 
	m_camDir.normalize();
	m_camDis = getAttributeAsFloat("Distance");	

	//convert lookat to euler angles
	irr::core::matrix4 matRot;
	irr::core::vector3df up = m_camera->getUpVector();
	irr::core::vector3df right = -m_camDir.crossProduct(up);
	right.normalize();
	up = -right.crossProduct(m_camDir);
	up.normalize();
	matRot[0] = right.X;
	matRot[1] = right.Y;
	matRot[2] = right.Z;
	matRot[4] = m_camDir.X;
	matRot[5] = m_camDir.Y;
	matRot[6] = m_camDir.Z;
	matRot[8] = up.X;
	matRot[9] = up.Y;
	matRot[10] = up.Z;

	irr::core::vector3df rotVec = matRot.getRotationDegrees();
	m_yaw = rotVec.Z;
	m_pitch = rotVec.X;
	m_roll = rotVec.Y;
	

	updateCamera();

	m_leftMousePressed = false;
	m_rightMousePressed = false;
	m_middleMousePressed = false;
}

void CChangeCameraAnimator::updateCamera()
{	
	//compute dir from euler
	irr::core::matrix4 matRot;
	matRot.setRotationDegrees(irr::core::vector3df(m_pitch, m_roll, m_yaw));
	m_camDir.set(matRot[4],matRot[5],matRot[6]);
	m_camDir.normalize();		

	irr::core::vector3df camTarget = m_targetPos;	

	irr::core::vector3df camPos = camTarget-m_camDir*m_camDis;

	m_camera->setPosition(camPos);
	m_camera->setTarget(camTarget);
	m_camera->updateAbsolutePosition();
}

void CChangeCameraAnimator::SaveCameraInfoToCinematic()
{	
	setAttribute("target", m_targetPos);
	setAttribute("dir", m_camDir);	
	setAttribute("Distance", m_camDis);		

	ui->m_CinematicEditor.UpdateCurrentCinematic();

	m_leftMousePressed = false;
	m_rightMousePressed = false;
	m_middleMousePressed = false;
}

void CChangeCameraAnimator::resetCamera()
{
	if(m_cmd!=0)
		SetCameraInfo(m_cmd);
}
